#define INDEX_FILE_NAME         "_disk.index"
#define RAW_VECTOR_FILE_NAME    "_mem.index.data"
#define PAGE_SIZE 16384

#define ASSERT_NULL_POINTER(ptr, message) \
    do { \
        if ((ptr) == NULL) { \
            printf("[ERROR!]%s in %s is NULL\n", message, __func__); \
            exit(EXIT_FAILURE); \
        } \
    } while(0)

typedef struct DiskANN_index
{
    unsigned long entry_count; //入口点的数量
    unsigned long* entries_id; //入口点的id

    unsigned long total_vectors; 
    unsigned long dim;
    unsigned long neighbor_count;
    unsigned long bytes_per_dim; //每个特征的大小
    unsigned long bytes_per_vector;
    unsigned long vector_per_block;
    unsigned long total_index_file_size;
    unsigned long block_size;    //索引文件的块大小，不是磁盘的块大小
} INDEX_HEAD;

void print_DiskANN_index(INDEX_HEAD* head){
    printf("========================\n");

    printf("total_vectors:%lu\n",head->total_vectors);
    printf("dim:%lu\n",head->dim);
    printf("neighbor_count:%lu\n",head->neighbor_count);
    printf("bytes_per_dim:%lu\n",head->bytes_per_dim);
    printf("bytes_per_vector:%lu\n",head->bytes_per_vector);
    printf("vector_per_block:%lu\n",head->vector_per_block);
    printf("total_index_file_size:%lu\n",head->total_index_file_size);
    printf("entry_count:%lu\n",head->entry_count);
    printf("entries_id:");
    for(int i=0; i<head->entry_count; i++)
        printf("%lu,",head->entries_id[i]);
    printf("\n");

    printf("========================\n");
}

void destory_DiskANN_index(INDEX_HEAD* head){
    free(head->entries_id);
}

//加载索引文件的头，不读取实际向量
//需要为每个数据集定制一个该函数，尽量把与数据集相关的字段修改都在这个函数里面完成
int load_index_file_SIFT1M(FILE *fp, INDEX_HEAD* head){
    ASSERT_NULL_POINTER(fp, "fp");
    ASSERT_NULL_POINTER(head, "head");

    fseek(fp, 0, SEEK_SET);
    head->entry_count = 1;
    head->bytes_per_dim = 4; 
    head->block_size = 4096; //设置数据集的外部参数（无法从文件中读取到的）

    long tmp; //用来接收没用的字段
    fread(&tmp, 8, 1, fp);//0-7

    fread(&(head->total_vectors), 8, 1, fp); // 8-15
    fread(&(head->dim), 8, 1, fp);  //16-23
    head->entries_id = (unsigned long*)malloc(sizeof(unsigned long)*(head->entry_count));
    fread(head->entries_id, 8, head->entry_count, fp);
    fread(&(head->bytes_per_vector), 8, 1, fp);
    fread(&(head->vector_per_block), 8, 1, fp);

    fread(&tmp, 8, 1, fp);
    fread(&tmp, 8, 1, fp);
    fread(&tmp, 8, 1, fp);
    fread(&(head->total_index_file_size), 8, 1, fp);

    //读取邻居数。我们假设每个节点的邻居数量是固定的
    fseek(fp, 4096+(head->dim)*(head->bytes_per_dim), SEEK_SET);
    fread(&(head->neighbor_count), 4, 1, fp);

    print_DiskANN_index(head);
    return 0;
}

//存储向量及其邻居，原始DiskANN和VectorSSD共用这个结构体。
//他们的区别主要是是否包含了邻居的聚类ID
typedef struct neighbor
{
    unsigned int id; //4B
    unsigned short cluster_id; //2B
} NEIGHBOR;

typedef struct vector
{
    float* features; //指向向量特征
    struct neighbor* neighbors; //指向邻居列表
    void* buf; //指向读取到的原始向量数据。其他的字段都是基于该字段的偏移
} VECTOR;

//根据之前读取到的DiskANN_index信息，创建、初始化一个向量并返回
VECTOR* create_vector_buffer(INDEX_HEAD* head){
    ASSERT_NULL_POINTER(head, "head");

    VECTOR* ret = (VECTOR*)malloc(sizeof(VECTOR));
    ret->buf = malloc(head->bytes_per_vector);
    ret->features = ret->buf;
    ret->neighbors = ret->buf+((head->bytes_per_dim)*(head->dim)+4)/4;

    return ret;
}

//根据id从disk.index里面读取向量.buf的空间需要自己分配
//head需要首先通过load_index_file_SIFT1M读取
int read_vector_fromfile(FILE *fp, INDEX_HEAD* head, unsigned int vid, VECTOR* vector){
    ASSERT_NULL_POINTER(fp, "fp");
    ASSERT_NULL_POINTER(head, "head");
    ASSERT_NULL_POINTER(vector, "buf");


    return 0;
}

//销毁向量
void destroy_vector(VECTOR* vector){
    free(vector->buf);
    free(vector);
}